package com.df.ez_game.generator;

import com.baidu.bjf.remoting.protobuf.annotation.ProtobufClass;
import com.df.ez_game.business.annotation.EzController;
import com.df.ez_game.business.annotation.EzMapping;
import com.df.ez_game.server.msg.client.ClientReqMsg;
import com.df.ez_game.server.msg.client.ClientRespMsg;
import com.df.ez_game.server.msg.wrapper.*;
import com.google.protobuf.ByteString;
import com.google.protobuf.BytesValue;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;

@Slf4j
public class CodeGenerator1 {
    public static void main(String[] args) throws IOException {
        //jprotobuf的生成示例
        //https://github.com/jhunters/jprotobuf/blob/master/Document.md
        //返回的内容即为 Protobuf的IDL描述文件
//        String code = ProtobufIDLGenerator.getIDL(WearEquipReq.class);
//        System.out.println(code);

        //iogame的封装示例
        //https://iohao.github.io/game/docs/extension_module/jprotobuf/
//        GenerateFileKit.generate("com.iohao.example.sdk.logic.data");
//        ProtoGenerate
        //生产消息定义
        //生产客户端SDK 生产ts文件


        //TODO 我们自己需要实现的
        //全局配置，比如超时时间，全局版本等

        //生成ezgame的核心代码
        //直接拷贝提前写好的，存在则不拷贝

        //生成每个controller文件对应的api文件
        //收集所有controller
        //忽略的模块
        Set<Integer> skipModules = new HashSet<>();
        //忽略的方法
        SkipMethod skipMethod = new SkipMethod();

        //请求，响应，广播的实体类，用于生成proto
        Set<Class> reqAndRespEntity = new HashSet<>();
        //系统提供的包装类型
        Set<Class> ezGameEntity = new HashSet<>();
        ezGameEntity.add(BoolWrapper.class);
        ezGameEntity.add(BoolListWrapper.class);
        ezGameEntity.add(IntWrapper.class);
        ezGameEntity.add(IntListWrapper.class);
        ezGameEntity.add(LongWrapper.class);
        ezGameEntity.add(LongListWrapper.class);
        ezGameEntity.add(StringWrapper.class);
        ezGameEntity.add(StringListWrapper.class);
        ezGameEntity.add(ObjectListWrapper.class);
        ezGameEntity.add(ClientReqMsg.class);
        ezGameEntity.add(ClientRespMsg.class);

        //TODO 先收集内部请求的方法id，放置用户的id与系统的重复了

        List<ControllerCodeMeta> controllerCodeMetas = new ArrayList<>();

        String controllerScanPackage = "com.df.ez_game.server.logic.module";
        Reflections reflections = new Reflections(controllerScanPackage);
        Set<Class<?>> classes = reflections.getTypesAnnotatedWith(EzController.class);

        for (Class<?> aClass : classes) {
            //获取controller名
            EzController annotation = aClass.getAnnotation(EzController.class);
            if(annotation.internal()){
                continue;
            }
            int id = annotation.id();
            if(skipModules.contains(id)){//主动忽略的
                continue;
            }
            controllerClassHandle(annotation,id,aClass,controllerCodeMetas,reqAndRespEntity,skipMethod);
        }
        //proto生成
        protoGenerator(reqAndRespEntity,ezGameEntity);
        //生成api代码
        apiCodeGenerator(controllerCodeMetas);
        //生成推送监听代码


    }

    private static void apiCodeGenerator(List<ControllerCodeMeta> controllerCodeMetas) {

    }

    private static class SkipMethod{
        Map<Integer,Set<Integer>> skips = new HashMap<>();

        public void addSkip(Integer module,Integer...methodIds){
            if(methodIds.length==0){
                return;
            }
            Set<Integer> integers = skips.get(module);
            if(integers==null){
                integers = new HashSet<>();
                skips.put(module,integers);
            }
            for (Integer methodId : methodIds) {
                integers.add(methodId);
            }
        }
        public boolean isSkip(Integer module,Integer methodId){
            Set<Integer> integers = skips.get(module);

            if(integers==null){
                return false;
            }
            if(integers.contains(methodId)){
                return true;
            }
            return false;
        }
    }

    private static void controllerClassHandle(EzController annotation, int id, Class<?> aClass, List<ControllerCodeMeta> controllerCodeMetas, Set<Class> reqAndRespEntity, SkipMethod skipMethod) {

        int version = annotation.version();
        String desc = annotation.desc();
        ControllerCodeMeta controllerCodeMeta = new ControllerCodeMeta();
        controllerCodeMeta.setId(id)
                .setName(aClass.getSimpleName().replace("Controller",""))
                .setDesc(desc)
                .setVersion(version);
        //方法信息 public表示对客户端开放，private
        Method[] declaredMethods = aClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            EzMapping mapping = declaredMethod.getAnnotation(EzMapping.class);
            if(mapping==null){
                continue;
            }
            if(mapping.internal()){
                continue;
            }
            if(skipMethod.isSkip(annotation.id(),mapping.id())){
                continue;
            }
            controllerMethodHandle(controllerCodeMetas,controllerCodeMeta,mapping,id,aClass,declaredMethod,reqAndRespEntity);
        }
        if(!controllerCodeMeta.methodCodeMetaMap.isEmpty()){
            controllerCodeMetas.add(controllerCodeMeta);
        }
    }

    private static void controllerMethodHandle(List<ControllerCodeMeta> controllerCodeMetas, ControllerCodeMeta controllerCodeMeta, EzMapping mapping, int id, Class<?> aClass, Method declaredMethod, Set<Class> reqAndRespEntity) {
        int methodId = mapping.id();
        String methodDesc = mapping.desc();
        int methodVer = mapping.version();
        int fullId = (id & 0xffff) << 16 | (methodId & 0xffff);
        if(controllerCodeMeta.getMethodCodeMetaMap().containsKey(fullId)){
            log.error("消息id重复："+aClass.getName()+" 方法名："+declaredMethod.getName());
            System.exit(-1);
        }
        MethodCodeMeta methodCodeMeta = new MethodCodeMeta();
        //请求
        Type[] parameters = declaredMethod.getGenericParameterTypes();

        if(parameters.length>1){
            log.error("方法的参数超过1个！"+aClass.getName()+" 方法名："+declaredMethod.getName());
            System.exit(-1);
        }
        methodCodeMeta.setHavaParams(false);
        if(parameters.length==1){//有请求参数
            methodCodeMeta.setHavaParams(true);
            Type parameter = parameters[0];
            //参数解析
            parameterHandle(parameter,methodCodeMeta,declaredMethod,reqAndRespEntity);
        }

        methodCodeMeta.setMethodId(methodId)
                .setFullId(fullId)
                .setMethodName(declaredMethod.getName())
                .setDesc(methodDesc)
                .setVersion(methodVer);

        //响应
        Type genericReturnType = declaredMethod.getGenericReturnType();
        methodCodeMeta.setHavaReturn(true);
        if(genericReturnType.getTypeName().equals("void")){
            methodCodeMeta.setHavaReturn(false);
        }else {//有返回值,支持的4种引用类型：Integer,Long,String,Boolean ，和参数解析差不读
            returnHanlde(genericReturnType,methodCodeMeta,reqAndRespEntity);
        }


        controllerCodeMeta.getMethodCodeMetaMap().put(methodCodeMeta.fullId,methodCodeMeta);
    }

    private static void setReturnClassName(MethodCodeMeta methodCodeMeta,String wrapperClassName,String rawClassName){
        methodCodeMeta.setReturnWrapperClassName(wrapperClassName);
        methodCodeMeta.setReturnRawClassName(rawClassName);
    }

    private static void returnHanlde(Type genericReturnType, MethodCodeMeta methodCodeMeta, Set<Class> reqAndRespEntity) {
        methodCodeMeta.setReturnIsWrapper(true);
        if(genericReturnType.equals(Integer.class)){
            setReturnClassName(methodCodeMeta,IntWrapper.class.getSimpleName(),Integer.class.getSimpleName());
        } else if(genericReturnType.equals(Long.class)){
            setReturnClassName(methodCodeMeta,LongWrapper.class.getSimpleName(),Long.class.getSimpleName());
        }else if(genericReturnType.equals(String.class)){
            setReturnClassName(methodCodeMeta,StringWrapper.class.getSimpleName(),String.class.getSimpleName());
        }else if(genericReturnType.equals(Boolean.class)){
            setReturnClassName(methodCodeMeta,BoolWrapper.class.getSimpleName(),Boolean.class.getSimpleName());
        }else if(genericReturnType instanceof ParameterizedType){//有泛型的类型
            ParameterizedType impl = (ParameterizedType) genericReturnType;
            Type rawType = impl.getRawType();
            if(rawType.equals(List.class) || rawType instanceof List){//解析list里的泛型
                Type[] actualTypeArguments = impl.getActualTypeArguments();
                Type actualTypeArgument = actualTypeArguments[0];
                if(actualTypeArgument.equals(Integer.class)){
                    setReturnClassName(methodCodeMeta,IntListWrapper.class.getSimpleName(),Integer.class.getSimpleName());
                }else if(actualTypeArgument.equals(Boolean.class)){
                    setReturnClassName(methodCodeMeta,BoolListWrapper.class.getSimpleName(),Boolean.class.getSimpleName());
                }else if(actualTypeArgument.equals(String.class)){
                    setReturnClassName(methodCodeMeta,StringListWrapper.class.getSimpleName(),String.class.getSimpleName());
                }else if(actualTypeArgument.equals(Long.class)){
                    setReturnClassName(methodCodeMeta,LongListWrapper.class.getSimpleName(),Long.class.getSimpleName());
                }else {//自定义的类
                    Class actualClass = (Class) actualTypeArgument;
                    Annotation fanxingAnnotaion = actualClass.getAnnotation(ProtobufClass.class);
                    if(fanxingAnnotaion==null){
                        log.warn("返回值list的泛型没有被@ProtobufClass注解，无法处理！");
                        System.exit(-1);
                    }else {
                        setReturnClassName(methodCodeMeta,ObjectListWrapper.class.getSimpleName(),actualClass.getSimpleName());
                        reqAndRespEntity.add(actualClass);
                    }
                }

            }else {
                log.error("未支持的返回值类型："+rawType);
                System.exit(-1);
            }
        }else {//自定义类型
            methodCodeMeta.setReturnIsWrapper(false);
            Class returnClass = (Class) genericReturnType;
            Annotation returnClassAnnotation = returnClass.getAnnotation(ProtobufClass.class);
            if(returnClassAnnotation==null){
                log.error("未支持的返回值类型或返回值没有被@ProtobufClass注解");
                System.exit(-1);
            }else {
                methodCodeMeta.setReturnRawClassName(returnClass.getSimpleName());
                reqAndRespEntity.add(returnClass);
            }

        }
    }

    private static void setParameterClassName(MethodCodeMeta methodCodeMeta,String wrapperClassName,String rawClassName){
        methodCodeMeta.setParamWrapperClassName(wrapperClassName);
        methodCodeMeta.setParamRawClassName(rawClassName);
    }
    private static void parameterHandle(Type parameter, MethodCodeMeta methodCodeMeta, Method declaredMethod, Set<Class> reqAndRespEntity) {
        methodCodeMeta.setParamIsWrapper(true);
        //只支持引用类型
        if(parameter.equals(Integer.class)){
            setParameterClassName(methodCodeMeta,IntWrapper.class.getSimpleName(),Integer.class.getSimpleName());
        }else if(parameter.equals(Boolean.class)){
            setParameterClassName(methodCodeMeta,BoolWrapper.class.getSimpleName(),Boolean.class.getSimpleName());
        }else if(parameter.equals(String.class)){
            setParameterClassName(methodCodeMeta,StringWrapper.class.getSimpleName(),String.class.getSimpleName());
        }else if(parameter.equals(Long.class)){
            setParameterClassName(methodCodeMeta,LongWrapper.class.getSimpleName(),Long.class.getSimpleName());
        }else if(parameter instanceof ParameterizedType){//支持list类型
            ParameterizedType impl = (ParameterizedType) parameter;
            Type rawType = impl.getRawType();
            if(rawType.equals(List.class) || rawType instanceof List){//解析list里的泛型
                Type[] actualTypeArguments = impl.getActualTypeArguments();
                Type actualTypeArgument = actualTypeArguments[0];
                if(actualTypeArgument.equals(Integer.class)){
                    setParameterClassName(methodCodeMeta,IntListWrapper.class.getSimpleName(),Integer.class.getSimpleName());
                }else if(actualTypeArgument.equals(Boolean.class)){
                    setParameterClassName(methodCodeMeta,BoolListWrapper.class.getSimpleName(),Boolean.class.getSimpleName());
                }else if(actualTypeArgument.equals(String.class)){
                    setParameterClassName(methodCodeMeta,StringListWrapper.class.getSimpleName(),String.class.getSimpleName());
                }else if(actualTypeArgument.equals(Long.class)){
                    setParameterClassName(methodCodeMeta,LongListWrapper.class.getSimpleName(),Long.class.getSimpleName());
                }else {//自定义的类
                    Class actualClass = (Class) actualTypeArgument;
                    Annotation fanxingAnnotaion = actualClass.getAnnotation(ProtobufClass.class);
                    if(fanxingAnnotaion==null){
                        log.warn("参数list的泛型没有被@ProtobufClass注解，无法处理！");
                        System.exit(-1);
                    }else {
                        setParameterClassName(methodCodeMeta,ObjectListWrapper.class.getSimpleName(),actualClass.getSimpleName());
                        reqAndRespEntity.add(actualClass);
                    }
                }

            }else {
                log.error("未支持的参数类型："+parameter);
                System.exit(-1);
            }

        }else {//自定义类型
            methodCodeMeta.setParamIsWrapper(false);
            Class<?> parameterType = declaredMethod.getParameterTypes()[0];
            ProtobufClass protoClass = parameterType.getAnnotation(ProtobufClass.class);
            if(protoClass==null){
                log.error("未支持的参数类型："+parameterType);
                System.exit(-1);
            }else {
                methodCodeMeta.setParamRawClassName(parameterType.getSimpleName());
                reqAndRespEntity.add(parameterType);
            }

        }
    }

    private static void protoGenerator(Set<Class> reqAndRespEntity, Set<Class> ezGameEntity) {
        Set<Class> handleClass = new HashSet<>(reqAndRespEntity);
        StringBuilder sb = new StringBuilder();
        sb.append("syntax = \"proto3\";\n");
        sb.append("package common;\n");
        sb.append("\n\n");

        while (!handleClass.isEmpty()){
            Set<Class> copy = new HashSet<>(handleClass);
            handleClass.clear();
            for (Class aClass : copy) {
                String messageName = aClass.getSimpleName();//消息的名字
                //TODO 检测这个类是枚举还是类
                //消息开始
                sb.append("message ").append(messageName).append(" {\n");
                //字段
                Field[] declaredFields = aClass.getDeclaredFields();
                int order = 0;
                for (Field declaredField : declaredFields) {
                    order++;
                    Type genericType = declaredField.getGenericType();
                    // Integer int32
                    // String string
                    // Boolean bool
                    // Long int64
                    if(genericType.equals(Integer.class)){
                        sb.append("  ").append("int32 ").append(declaredField.getName()).append(" = ").append(order).append(";\n");
                    }else if(genericType.equals(Long.class)){
                        sb.append("  ").append("int64 ").append(declaredField.getName()).append(" = ").append(order).append(";\n");
                    }else if(genericType.equals(String.class)){
                        sb.append("  ").append("string ").append(declaredField.getName()).append(" = ").append(order).append(";\n");
                    }else if(genericType.equals(Boolean.class)){
                        sb.append("  ").append("bool ").append(declaredField.getName()).append(" = ").append(order).append(";\n");
                    }else if(genericType instanceof ParameterizedType){//支持list类型
                        //带泛型的类型


                    }
                    System.out.println(1);
                }
                //消息结束
                sb.append("}\n");
                sb.append("\n");
            }
        }
        try {
            Files.writeString(Path.of("D:\\My\\cocos\\esgame_cocos_demo\\demo1\\proto\\equip.proto"),sb.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
        sb = new StringBuilder();
        sb.append("syntax = \"proto3\";\n");
        sb.append("package ezgame;\n");
        sb.append("\n\n");
        //TODO 系统内置的类型生成到ezgame.proto,提前生成好，下次直接复制生成好的代码
        for (Class aClass : ezGameEntity) {
            String messageName = aClass.getSimpleName();//消息的名字
            //TODO 检测这个类是枚举还是类
            //消息开始
            sb.append("message ").append(messageName).append(" {\n");
            //字段
            Field[] declaredFields = aClass.getDeclaredFields();
            int order = 0;
            for (Field declaredField : declaredFields) {
                order++;
                Type genericType = declaredField.getGenericType();
                // Integer int32
                // String string
                // Boolean bool
                // Long int64
                if(genericType.equals(Integer.class)){
                    sb.append("  ").append("int32 ").append(declaredField.getName()).append(" = ").append(order).append(";\n");
                }else if(genericType.equals(Boolean.class)){
                    sb.append("  ").append("bool ").append(declaredField.getName()).append(" = ").append(order).append(";\n");
                }else if(genericType.equals(Long.class)){
                    sb.append("  ").append("int64 ").append(declaredField.getName()).append(" = ").append(order).append(";\n");
                }else if(genericType.equals(String.class)){
                    sb.append("  ").append("string ").append(declaredField.getName()).append(" = ").append(order).append(";\n");
                }else if(genericType.getTypeName().equals("byte[]")){//字节数组
                    sb.append("  ").append("bytes ").append(declaredField.getName()).append(" = ").append(order).append(";\n");
                }else if(genericType instanceof ParameterizedType){
                    ParameterizedType parameterizedType = (ParameterizedType) genericType;
                    Type[] fanxingTypes = parameterizedType.getActualTypeArguments();
                    Type fanxingType = fanxingTypes[0];
                    Type rawType = parameterizedType.getRawType();
                    if(rawType.equals(List.class)){
                        if(fanxingType.equals(Integer.class)){
                            sb.append("  ").append("repeated int32 ").append(declaredField.getName()).append(" = ").append(order).append(";\n");
                        }else if(fanxingType.equals(Boolean.class)){
                            sb.append("  ").append("repeated bool ").append(declaredField.getName()).append(" = ").append(order).append(";\n");
                        }else if(fanxingType.equals(Long.class)){
                            sb.append("  ").append("repeated int64 ").append(declaredField.getName()).append(" = ").append(order).append(";\n");
                        }else if(fanxingType.equals(String.class)){
                            sb.append("  ").append("repeated string ").append(declaredField.getName()).append(" = ").append(order).append(";\n");
                        }else if(fanxingType.equals(ByteString.class)){
                            sb.append("  ").append("repeated bytes ").append(declaredField.getName()).append(" = ").append(order).append(";\n");
                        }
                    }
                }else {
                    Class msgClass = (Class) genericType;
                    Annotation annotation = msgClass.getAnnotation(ProtobufClass.class);
                    if(annotation==null){
                        log.error("内置类型没有被@ProtobufClass注解："+msgClass.getName());
                        System.exit(-1);
                    }

                }
            }
            //消息结束
            sb.append("}\n");
            sb.append("\n");
        }
        try {
            Files.writeString(Path.of("D:\\My\\cocos\\esgame_cocos_demo\\demo1\\proto\\ezgame.proto"),sb.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Data
    @Accessors(chain = true)
    private static class ControllerCodeMeta{
        private Integer id;
        private String name;
        private Integer version;
        private String desc;
        Map<Integer,MethodCodeMeta> methodCodeMetaMap = new HashMap<>();
    }

    @Data
    @Accessors(chain = true)
    private static class MethodCodeMeta{
        private Integer methodId;
        private String methodName;
        private Integer fullId;
        private Integer version;
        private String desc;


        private boolean havaParams;
        //参数是否是包装类型
        private boolean paramIsWrapper;
        //包装类名
        private String paramWrapperClassName;
        //原始类名
        private String paramRawClassName;


        private boolean havaReturn;
        private boolean returnIsWrapper;
        private String returnWrapperClassName;
        private String returnRawClassName;
    }
}
